/**
 * Copyright (c) 2008 riaevolution.com, All Rights Reserved.
 *
 * See www.riaevolution.com
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 * */
package com.riaevolution.components.coverflow {
	import com.dougmccune.containers.CoverFlowContainer;

	import flash.events.Event;
	import flash.events.IEventDispatcher;
	import flash.events.KeyboardEvent;
	import flash.ui.Keyboard;
	import flash.utils.clearInterval;
	import flash.utils.setInterval;

	import mx.collections.ArrayCollection;
	import mx.collections.IList;
	import mx.collections.XMLListCollection;
	import mx.containers.Canvas;
	import mx.controls.Image;
	import mx.core.ClassFactory;
	import mx.core.IDataRenderer;
	import mx.core.IFactory;
	import mx.core.UIComponent;
	import mx.events.CollectionEvent;
	import mx.events.FlexEvent;
	import mx.managers.IFocusManagerComponent;

	/**
	 *
	 * Cover flow extends the default data CoverFlowContainer by dougt macunne.
	 * Allow you to treat this as a List componente of flex.
	 *
	 *
	 * <br /><b>Luciano bustos</b> <a href="mailto:luchyx&#64;riaevolution.com">luchyx&#64;riaevolution.com</a><br /><a href="http://www.riaevolution.com">www.riaevolution.com</a>
	 *
	 */
	public class CoverFlow extends CoverFlowContainer implements IFocusManagerComponent {

		public function CoverFlow() {
			super();

			addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);

			dataProvider = new ArrayCollection();
			itemRenderer = new ClassFactory(Image);
		}

		private var _dataProviderChanged:Boolean = false;

		private var _dataProvider:Object;

		/**
		 * Commit the properties.
		 */
		override protected function commitProperties():void {
			super.commitProperties();

			if (_dataProviderChanged) {
				_dataProviderChanged = false;
				var i:int;

				for (i = numChildren - 1; i >= 0; i--) {
					removeChildAt(numChildren - 1);
				}

				for (i = 0; i < _dataProvider.length; i++) {
					var ui_instance:UIComponent = UIComponent(itemRenderer.newInstance());
					IDataRenderer(ui_instance).data = _dataProvider.getItemAt(i);

					var container_ins:Canvas = new Canvas();
					container_ins.width = itemWidth;
					container_ins.height = itemHeight;

					ui_instance.percentHeight = ui_instance.percentWidth = 100;

					container_ins.addChild(ui_instance);
					addChild(container_ins);
				}

				//enterFrameHandler();
				if (_internalTime) {
					clearInterval(_internalTime);
				}
				_internalTime = setInterval(internal_Handler, 500);
				internal_Handler();
			}

			if (_itemWidthChanged) {
				_itemWidthChanged = false;
					//update components width
			}

			if (_itemHeightChanged) {
				_itemHeightChanged = false;
					//update components hieght				
			}

		}

		private var _itemWidthChanged:Boolean = false;

		private var _itemWidth:Number = 120;

		/**set the item width
		 *
		 * @return void
		 */
		public function set itemWidth(value:Number):void {
			_itemWidth = value;
			_itemWidthChanged = true;
			invalidateProperties();
		}

		/**
		 * get the itemWidths
		 * @return Number
		 */

		public function get itemWidth():Number {
			return _itemWidth;
		}

		private var _itemHeightChanged:Boolean = false;

		private var _itemHeight:Number = 120;

		/**
		 * Set the itemHeight
		 *
		 * @return void
		 */
		public function set itemHeight(value:Number):void {
			_itemHeight = value;
			_itemHeightChanged = true;
			invalidateProperties();
		}

		/**
		 *
		 * @return Number
		 */
		public function get itemHeight():Number {
			return _itemHeight;
		}


		private var _internalTime:int;

		/**
		 * Especify the dataProvider for image displaying
		 *
		 * @return void
		 */
		[Bindable]
		public function set dataProvider(value:Object):void {
			/**
			 * first, if we have a previous dataProvider, we're going to want to remove any event listeners from it
			 */
			if (_dataProvider != null) {
				_dataProvider.removeEventListener(CollectionEvent.COLLECTION_CHANGE, _dataChangeHandler, false);
			}

			/**
			 * Now, as a convenience to the caller, convert our dataProvider into an IList implementation.
			 **/
			if (value is Array) {
				_dataProvider = new ArrayCollection(value as Array);
			} else if (value is IList) {
				_dataProvider = IList(value);
			} else if (value is XMLList) {
				_dataProvider = new XMLListCollection(value as XMLList);
			}
			/**
			 * Add an event listener so we know and can react when our dataProvider changes. Note that the convention in flex is that
			 * list-like components are only responsible for detecting and reacting to changes in the list itself, _not_ changes
			 * to the properties of the items themselves.  It's the responsibility of the item renderers to do that as necessary.
			 *
			 * Also, note that we're using a weak listener here. Since the data provider is being passed in by an external caller,
			 * we don't know what the lifetime of the dataProvider is w/relation to our lifetime. Since we don't have a constructor,
			 * we won't ever get a chance to remove our listener. So we use a weak listener to make sure we don't get locked into memory by this.
			 */
			_dataProvider.addEventListener(CollectionEvent.COLLECTION_CHANGE, _dataChangeHandler, false, 0, true);
			/**
			 * since we now need to re-allocate our item renderers, we'll set a flag and invalidate our properties.
			 * As with layout and size, by putting the item renderer generation into commitProperties, we avoid having to run it too often
			 */
			_dataProviderChanged = true;
			invalidateProperties();
			/**
			 * Our measured size is dependent on our number and size of items in the dataProvider, so we need to invalidate it here
			 **/
			invalidateSize();
		}

		/**
		 * the current dataprovider
		 * @return Object
		 */
		public function get dataProvider():Object {
			return _dataProvider;
		}

		/**
		 * storage for the item renderer factory, that will generate item renderer interfaces for us as necessary.
		 */
		private var _itemRenderer:IFactory;

		/**
		 * The UIComponent that we'll use to render our items.  Since we need to create multiple of these...one for each item in the
		 * dataprovider...we need not an itemRenderer, but a _factory_ that can create itemRenderers on demand.  That's why we type
		 * this property as an IFactory. IFactory is a special interface that signals to the compiler that we need an object that implements
		 * the factory pattern. When the MXML compiler sees a property of type IFactory, it allows the developer specify it's value in one
		 * of three ways:
		 *    By specifying an object that implements the IFactory interface (that's normal).
		 *    By specifying the name of a class...it automatically wraps the class in an instance of ClassFactory and assigns that to the property.
		 *    By defining a component inline via <mx:Component>...it defines an implicit class, wraps it in a ClassFactory instance, and assigns that.
		 */
		public function set itemRenderer(value:IFactory):void {
			_itemRenderer = value;
			/**
			 * store off the value, and set the flag to say that we need to re-generate all of our item renderers
			 **/
			_dataProviderChanged = true;
			invalidateProperties();
			invalidateSize();
		}

		/**
		 * Return the current item renderer
		 * @return
		 */
		public function get itemRenderer():IFactory {
			return _itemRenderer;
		}

		/**
		 * dataChangeHandler
		 *
		 * @param event
		 */
		private function _dataChangeHandler(event:CollectionEvent):void {
			_dataProviderChanged = true;
			invalidateProperties();
		}

		private function internal_Handler(event:Event = null):void {
			var iui:IEventDispatcher = this.getChildAt(this.numChildren - 1);
			iui.dispatchEvent(new FlexEvent(FlexEvent.UPDATE_COMPLETE));
		}

		/**
		 * Key Down Handler.
		 * Only do the default thing if the target is the CoverFlow.
		 * Else you maybe be inside an interactive componente.
		 *
		 * @param event
		 *
		 */
		override protected function keyDownHandler(event:KeyboardEvent):void {
			if (event.currentTarget != event.target)
				return;

			if (event.keyCode == Keyboard.RIGHT || event.keyCode == Keyboard.UP) {
				if (selectedIndex < numChildren - 1) {
					selectedIndex++;
				} else {
					selectedIndex = 0;
				}
			} else if (event.keyCode == Keyboard.LEFT || event.keyCode == Keyboard.DOWN) {
				if (selectedIndex > 0) {
					selectedIndex--;
				} else {
					selectedIndex = numChildren - 1;
				}
			} else if (event.keyCode == Keyboard.HOME) {
				selectedIndex = 0;
			} else if (event.keyCode == Keyboard.END) {
				selectedIndex = numChildren - 1;
			}
		}
	}
}